#!/usr/bin/env python3
"""
数美SDK返回值解密工具
尝试所有可能的密钥派生方案
"""

import json
import zlib
import base64
import hashlib
from Crypto.Cipher import AES, PKCS1_v1_5
from Crypto.Util.Padding import unpad
from Crypto.PublicKey import RSA
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa

# ==================== 配置 ====================

CERTIFICATE_BASE64 = "MIIC6DCCadCgAwIBAgIGAZlhQblOMA0GCSqGSIb3DQEBCwUAMDUxCzAJBgNVBAYTAkNOMRAwDgYDVQQKEwdFeGFtcGxlMRQwEgYDVQQDEwtTZWxmLVNpZ25lZDAeFw0yNTA5MTkwOTE1MjFaFw0yNjA5MTkwOTE1MjFaMDUxCzAJBgNVBAYTAkNOMRAwDgYDVQQKEwdFeGFtcGxlMRQwEgYDVQQDEwtTZWxmLVNpZ25lZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALPwnK6bMFFHWeN3E/MIWmpZesBQGILjJxNtMN1Z2lmRyLCJdTmz87EXmLuX4e/Bz0aCOmH88qjs+laHFmnPIQal3rJU32MCMk63bejZN6yArwv8MAyF0kvyA3EoBC6ZsYX55sxtxLyNYvG8MCqmacRbxV0IsSMXq6o4+1wOYiQEXzdbqhp67fYPNOv81LXqxQZArGV+Wj2o86XtW1wixtHnaSUori4jJjbe1YKi6LIbzhz9SK34+V4PkRhQYS7ensgAAbm4k3N5R3RSNkOHLrHu576QaBwKYI1DjNJL9tAcySdqXColS9RLG0h/x2r78Pvybt3hm32f1kPbu3hnaMcCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEApMy1ilW+EsqQZ+2VMyvmQgjfqoaat5kvNqdluLwtzISuY/dv1V2ccR3eShnta7hKO1WCT1WF3L31MLtDwWw/VEiWFDu3+H/9P9nWr7w6ocGVWCUwUoZwiJk5DuBKZYlmj8LgjAKk6bh3hi7Fs3Kbct1rLXUtL0wiqRF7y49tnjWKiEOzgIeHQnS6m2geS1aEpUQlsEm8vUanNuAmnWJppMpfJuWQ7XDPZfRDelyepkefWSdOtd3RKKWGhk70KM00Scmav4mrUYE1K8xIXUvYrc2ECpyvuJjZ6masXzoTRGN+bEvlOML/U6TTbXIttbqqnNe9iLrszUStBXjGkpJohA=="

PRIVATE_KEY_PEM = """-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCz8JyumzBRR1nj
dxPzCFpqWXrAUBiC4ycTbTDdWdpZkciwiXU5s/OxF5i7l+Hvwc9Ggjph/PKo7PpW
hxZpzyEGpd6yVN9jAjJOt23o2TesgK8L/DAMhdJL8gNxKAQumbGF+ebMbcS8jWLx
vDAqpmnEW8VdCLEjF6uqOPtcDmIkBF83W6oaeu32DzTr/NS16sUGQKxlflo9qPOl
7VtcIsbR52klKK4uIyY23tWCouiyG84c/Uit+PleD5EYUGEu3p7IAAG5uJNzeUd0
UjZDhy6x7ue+kGgcCmCNQ4zSS/bQHMknalwqJUvUSxtIf8dq+/D78m7d4Zt9n9ZD
27t4Z2jHAgMBAAECggEAAr15RVdrpvE1NzeLADpyVghCzEbr+KJI6AzTn6tMneyQ
Z8/QDy7kWSAI3WJ0uFf1Nhepl/BoKZZiQYsRFk9nK1i/SWvtcu6HoZc9fzw/ksrq
333ZpXcsOqfW0ZRQa/0/LNEfaKGLS2vDw/afrSaXmbvkB4SoXeZwYMk5Wq+FYxL/
alrNpg/lzdvBlsCsTDA0G2RZrUVaO5yRTLRisBU+i8yORgnGkEwWvKpZYSFogCHS
iYHiNXFy//C+sYWlK7DjmG8/+krKhqBRRfdeg7LFThHQpbEGTtueD57jw0PCo2yr
6dqTvW9Qys+aWVNJk7RHNaI0yBFKWPadaHi3/olzkQKBgQDmz/FDpztS0fjKDIOj
gDyE76Z6dRFenmKJM1eOFt+MniY6/vi4ylU51/Etm4k6dNJ1MVBoFfyT9X0U/4k7
GNNUfIxMCJztUgY6EaCCt+09wk8pIvmOlRCmtxGwvmVC4nJVgUj3XJQ+wseblAkk
W0+IuT3GKX0CdKFMU4q9aucLYwKBgQDHk3rQ8Ae4ci4oj+MehRI29SNpcXG6OHbr
Z2EdsgqiFC1o/qv0269jtmCP7IqkGMdhTQrVetTHjZNZJH2bvUZ4o/0YT2+TTFPI
kTb2/a0txUTBTKrbnAdXh/iPFZAGvBROEDN0MNKo/vGAfi0K836DeTyBIaYongm6
6Rn6pwnUTQKBgBveVaougfoxAhIbSrWuISCH8xjsE6nSA+G/Aj5Uwq8u1TzgVlWx
kHLIgQVZt0sImfSufJ/kr7eJt42WgRJSoAmedC4mCBSbh8bxI+lEne+MC5TS9UDi
/Ly0c/1cL8vQna93ScEcO4YMbJ97U1NBdyvx+eR4U/C89lDJ8YGHa9gzAoGAQ6sY
sHFCXOKyDeTDoFyEUYgKqrzhT7/HaofR4Oy2OEBZKUl4anx2WnvC/+m3FG6mY7Jo
ovuT29mABXCe+khR9aO8tBpy/WGa4t2B4nse1e8WIehp4i5kOuSKfZFVFUN+Kv3J
RHMtakmO/v9JLHZlBhT8U9hh61GygOJ6gYdTiN0CgYAFz9iPy/xqLOUwiTNp3Imz
hdZo9ol3Qlr9CfMWMFHqRA5AyYvly9ZzxTpUCpSA+qz6OXqAjJ0bhGQxW2KHpmcT
yNGOPPca2vCaW8Mb1NTQueXpUBqX7alIkrRjUUyULteLb8FtyYl7mR3TVIu3FoO5
anNwDZ+2y7R9WUEiOzY3NA==
-----END PRIVATE KEY-----"""

ORGANIZATION = "qvdTr7A9k1DIU04ha4eP"
APP_ID = "default"
TIMESTAMP = 1761287286183

ENCRYPTED_RESPONSE = {
    "organization": "qvdTr7A9k1DIU04ha4eP",
    "os": "android",
    "appId": "default",
    "encode": 2,
    "compress": 3,
    "data": "wHBy2LDIay6WCgEybO8bw/5OxmVd4N++OJpwr/Tgjw+TA8iZ4HAy4bN8UWtwmeGz+dqU/zX6myCireq+q465rqUqpXLhma2aEPqTaNvUTv/Y71kVpkMBkgrjMnVkIi/iBV5eKsrTPfLWO8bt0mSbLM8ghswC2m2YRInkqWXHrc8Nw0JsNjwCllOUdtwe3NuIx8oQuQ2fqj1G3g3xMp6B4sWBNAo+SjTNuKnS4chvbaD3RYnGvSLyNYu+sqbKeRNofCwVn0lVrjCAGtC9d5nqhFiGw0vWksPfmCRn7vRsl7LnqcXb+Fal/hhREBsaRJdMrst3vOEeQI6pt3RGKZ3VOsliNziKbLiNnXp2zY2fin32okAFwIOczPgeQA1yAM4FCnuT9FYAMn5ecAOY7WFhJPcOqVThy0giCJrBzpIzEHXTin+BWBO1PR8wMzBqpf6us9mhBZGzu40mzVN34xXK646aMr8f2DcD9yGO20v5icEz3OUcAeayMq+YKmBoypmvQUnaGa8pjr7RfvvajY+7pu6QHqRAQ3NLY1KjIoT3PKjabb7owQ1Txw5+Ajfjen7VEeF/Dq03nPMtNOpqD3tCdWDHWir+akJcEPtPzK4Bo2K3FrLBZt2igZD6/04jtwoAojHibKqUM1Rj5h2iijH65eAU1XKTpqDPNtcP5lwa1bFNV+GvuzHM+N4gcoxiELhQeG7xiUcsZrJvadkDO2z+If9vLkXTSdMKrqozUjLRapyXpULcVL8negHnj+2vKZVdoGiDhXlm23XDFjLY91ZqX2eGfGNmpJxftl83N6q4dyXpzD2gR/chHfm0CukVlL8P8aPAfEDcAsyQETc5oVLzetGVSkGBbQl+axZu8U2Ap9CXaTi1b2VcI3he19nvgG8XEFnwP6Tbicm5PDDsiRjo2MRqfuPLWkcSHTmBlS6w8yrzAvth9x/bBLg8XLs8M/B/dKSfOa9sbptfTMmEuuhWbnqCyqnFSUt8lsFlcUbJdK02TcMbd789pANnrKTTfFeOUehu5XReHng4DfV7f6boQmJPnZfwuR8qFwp7vwme235RTloktlkQu/2QFl2LmCv+/8Za43dEdFba9Ae6I1p+50zunyIjGTE6/9RT+g/zgagzgV/qDDSM5VF6E2Ic62W/KkpfSPMHAk5Jwg0LlsSMnFXYCynZoDZ5M8s62V15VaEII3s/6wPCu4lba/BxPanRtw60EVsvrJ7B0mnG+hcW8AGVeG1ZBIhgPH887/bY+05b0MnSkhjlrys8GKSaukVbtnrLJmjePt+LdLe6tH2NTwxjbHhJR028ZWaQKvdC5BD2hRflDMBPKL68D6YJ2bitqnKKdEqYHdCMXtOXh68XUqwfkoF3phQtYHyrqF4Wsgukf3/gAlt/yF0dXFTIUzoDTc34MEAaLFENVFDt4PUnYw2SRT8i/bhi3tcavu6Hnjs=",
    "tn": "BMWDCPVx97y8KsnGiff6u4lmEogvgO30LtR/QvfGyqQWadycCGkbGhL7jCKvJTHmlK0k+fDR0XKq8U2IINpo0+aiWg8+nwaXQofOsilfXgDzG0DK9WJuwfcOrzGUHBn4yGVXDj+SUklPWbq0fojYGLYendkSnSLYTzeYIusMyZ3HD2djXyUvekKz0cp39JgrD/Ovj2Nrwn4ffPHcIXgSjpd02QXn4LNb4/7q465mfS2oQ5fRqiRMcnxpUhSD3Ty15bXVKJpPVqlqpM+tcUcXVV4g7ggOux361BsPius8as1vd7wuK44EOJ56MJeATqROjECur2bWdftoWydjfZj5sw==",
    "ep": "qt0AJsNLEfayUx7S15jLfmQvMOpW6JJ+jCoxeXxn676R2OiN6xpLIQWSYe8ETFUF4+AaGrVY1wMCdyUPVaHM0J+8m+q/PUFgbiP3N8xSKQc4lIzzZuckbybYiFuLbEhcblmy162S5B70UJxzB6grhIVpsG/h0uw+JPaS2yTPpPVCiN/SImdOw2g12cun6niAwRZSQktZikzzbjwkRoiO8ehrwlAFJircnT1/1zba2oaGXLc/yU6cM2vWgJdyyoXJF4Mo/5XNBTDT+8yr1hPC5MKHWcTBB/isrRTD5CczInLgXHl48KKz18GkpPM7MML1GHb4pIjfTvu8hv/SLLW0sg=="
}

# ==================== 工具函数 ====================

def print_hex(data, label=""):
    """打印十六进制数据"""
    if label:
        print(f"{label}: {data.hex()}")
    else:
        print(data.hex())

def calculate_entropy(data):
    """计算数据熵"""
    from collections import Counter
    import math
    counter = Counter(data)
    length = len(data)
    entropy = -sum((count/length) * math.log2(count/length) for count in counter.values())
    return entropy

# ==================== 密钥派生方案 ====================

def derive_keys():
    """生成所有可能的密钥"""
    keys = []
    
    # 解析证书
    cert_data = base64.b64decode(CERTIFICATE_BASE64)
    cert = x509.load_der_x509_certificate(cert_data, default_backend())
    public_key = cert.public_key()
    
    # 方案1: SHA256(公钥DER)
    pub_key_der = public_key.public_bytes(
        encoding=serialization.Encoding.DER,
        format=serialization.PublicFormat.SubjectPublicKeyInfo
    )
    key1 = hashlib.sha256(pub_key_der).digest()
    keys.append(("SHA256(公钥DER)", key1))
    
    # 方案2: SHA256(模数N)
    if isinstance(public_key, rsa.RSAPublicKey):
        n = public_key.public_numbers().n
        n_bytes = n.to_bytes((n.bit_length() + 7) // 8, 'big')
        key2 = hashlib.sha256(n_bytes).digest()
        keys.append(("SHA256(模数N)", key2))
    
    # 方案3: 证书指纹
    cert_fingerprint = cert.fingerprint(hashlib.sha256())
    key3 = cert_fingerprint[:32]
    keys.append(("证书指纹", key3))
    
    # 方案4: SHA256(organization:appId)
    key4 = hashlib.sha256(f"{ORGANIZATION}:{APP_ID}".encode()).digest()
    keys.append(("SHA256(org:appId)", key4))
    
    # 方案5: MD5(organization:appId) 扩展到32字节
    key5_16 = hashlib.md5(f"{ORGANIZATION}:{APP_ID}".encode()).digest()
    key5 = key5_16 + key5_16  # 重复到32字节
    keys.append(("MD5(org:appId)x2", key5))
    
    # 方案6: SHA256(organization)
    key6 = hashlib.sha256(ORGANIZATION.encode()).digest()
    keys.append(("SHA256(org)", key6))
    
    # 方案7: 组合派生
    material = ORGANIZATION.encode() + APP_ID.encode() + cert_data[:64]
    key7 = hashlib.sha256(material).digest()
    keys.append(("SHA256(org+appId+cert)", key7))
    
    # 方案8: 只用证书前32字节
    key8 = cert_data[:32]
    keys.append(("证书前32字节", key8))
    
    return keys

def derive_ivs(field_type="data"):
    """生成所有可能的IV"""
    ivs = []
    
    # 方案1: MD5(timestamp)
    iv1 = hashlib.md5(str(TIMESTAMP).encode()).digest()
    ivs.append(("MD5(timestamp)", iv1))
    
    # 方案2: MD5(field_type:timestamp)
    iv2 = hashlib.md5(f"{field_type}:{TIMESTAMP}".encode()).digest()
    ivs.append((f"MD5({field_type}:timestamp)", iv2))
    
    # 方案3: MD5(timestamp:org)
    iv3 = hashlib.md5(f"{TIMESTAMP}:{ORGANIZATION}".encode()).digest()
    ivs.append(("MD5(timestamp:org)", iv3))
    
    # 方案4: MD5(org)
    iv4 = hashlib.md5(ORGANIZATION.encode()).digest()
    ivs.append(("MD5(org)", iv4))
    
    # 方案5: MD5(appId)
    iv5 = hashlib.md5(APP_ID.encode()).digest()
    ivs.append(("MD5(appId)", iv5))
    
    # 方案6: 时间戳字节 + 填充
    timestamp_bytes = TIMESTAMP.to_bytes(8, 'big')
    iv6 = timestamp_bytes + timestamp_bytes
    ivs.append(("timestamp bytes", iv6))
    
    # 方案7: 全零IV
    iv7 = b'\x00' * 16
    ivs.append(("全零IV", iv7))
    
    # 方案8: MD5(org:appId)
    iv8 = hashlib.md5(f"{ORGANIZATION}:{APP_ID}".encode()).digest()
    ivs.append(("MD5(org:appId)", iv8))
    
    return ivs

# ==================== 解密函数 ====================

def decrypt_ep():
    """解密 ep 字段（RSA）"""
    print("\n" + "="*60)
    print("尝试解密 ep 字段（RSA-2048）")
    print("="*60)
    
    ep_bytes = base64.b64decode(ENCRYPTED_RESPONSE['ep'])
    print(f"ep 解码后长度: {len(ep_bytes)} 字节")
    print(f"熵值: {calculate_entropy(ep_bytes):.4f}")
    
    try:
        private_key = RSA.import_key(PRIVATE_KEY_PEM)
        cipher = PKCS1_v1_5.new(private_key)
        
        # 尝试解密
        sentinel = b"DECRYPTION_FAILED"
        decrypted = cipher.decrypt(ep_bytes, sentinel)
        
        if decrypted != sentinel:
            print("\n✅ ep 解密成功！")
            print("="*60)
            try:
                # 尝试作为字符串
                text = decrypted.decode('utf-8')
                print(f"解密内容（文本）:\n{text}")
                
                # 尝试解析JSON
                try:
                    json_obj = json.loads(text)
                    print(f"\n解密内容（JSON）:\n{json.dumps(json_obj, indent=2, ensure_ascii=False)}")
                except:
                    pass
            except:
                # 作为二进制
                print(f"解密内容（十六进制）:\n{decrypted.hex()}")
            return decrypted
        else:
            print("❌ ep 解密失败（使用哨兵值）")
    except Exception as e:
        print(f"❌ ep 解密失败: {e}")
    
    return None

def decrypt_symmetric_field(field_name, has_compression=False):
    """解密对称加密字段"""
    print("\n" + "="*60)
    print(f"尝试解密 {field_name} 字段（AES-CBC{' + zlib' if has_compression else ''}）")
    print("="*60)
    
    encrypted_bytes = base64.b64decode(ENCRYPTED_RESPONSE[field_name])
    print(f"{field_name} 解码后长度: {len(encrypted_bytes)} 字节")
    print(f"熵值: {calculate_entropy(encrypted_bytes):.4f}")
    print(f"前16字节: {encrypted_bytes[:16].hex()}")
    
    # 获取所有密钥和IV组合
    keys = derive_keys()
    ivs = derive_ivs(field_name)
    
    total_attempts = len(keys) * len(ivs)
    print(f"\n尝试 {len(keys)} 种密钥方案 × {len(ivs)} 种IV方案 = {total_attempts} 种组合")
    print("-"*60)
    
    attempt = 0
    for key_name, key in keys:
        for iv_name, iv in ivs:
            attempt += 1
            try:
                cipher = AES.new(key, AES.MODE_CBC, iv)
                decrypted = cipher.decrypt(encrypted_bytes)
                
                # 去除填充
                unpadded = unpad(decrypted, AES.block_size)
                
                # 如果需要解压缩
                if has_compression:
                    try:
                        decompressed = zlib.decompress(unpadded, -15)  # 原始 deflate
                        data = decompressed
                    except:
                        continue
                else:
                    data = unpadded
                
                # 尝试解析JSON
                try:
                    json_obj = json.loads(data.decode('utf-8'))
                    print(f"\n✅ {field_name} 解密成功！（尝试 {attempt}/{total_attempts}）")
                    print("="*60)
                    print(f"密钥方案: {key_name}")
                    print(f"密钥: {key.hex()}")
                    print(f"IV方案: {iv_name}")
                    print(f"IV: {iv.hex()}")
                    print(f"\n解密内容:\n{json.dumps(json_obj, indent=2, ensure_ascii=False)}")
                    return json_obj
                except:
                    # 尝试作为字符串
                    try:
                        text = data.decode('utf-8')
                        if len(text) > 10 and text.isprintable():
                            print(f"\n✅ {field_name} 解密成功（文本）！（尝试 {attempt}/{total_attempts}）")
                            print("="*60)
                            print(f"密钥方案: {key_name}")
                            print(f"IV方案: {iv_name}")
                            print(f"\n解密内容:\n{text}")
                            return text
                    except:
                        pass
            except Exception as e:
                continue
    
    print(f"\n❌ {field_name} 解密失败（尝试了 {total_attempts} 种组合）")
    return None

# ==================== 主函数 ====================

def main():
    print("="*60)
    print("数美SDK返回值解密工具")
    print("="*60)
    print(f"\n输入参数:")
    print(f"  organization: {ORGANIZATION}")
    print(f"  appId: {APP_ID}")
    print(f"  timestamp: {TIMESTAMP}")
    
    # 解密 ep（RSA）
    ep_result = decrypt_ep()
    
    # 解密 tn（AES）
    tn_result = decrypt_symmetric_field("tn", has_compression=False)
    
    # 解密 data（AES + zlib）
    data_result = decrypt_symmetric_field("data", has_compression=True)
    
    # 总结
    print("\n" + "="*60)
    print("解密结果总结")
    print("="*60)
    print(f"ep:   {'✅ 成功' if ep_result else '❌ 失败'}")
    print(f"tn:   {'✅ 成功' if tn_result else '❌ 失败'}")
    print(f"data: {'✅ 成功' if data_result else '❌ 失败'}")
    
    if ep_result or tn_result or data_result:
        print("\n💡 提示: 如果部分字段解密成功，记录对应的密钥和IV方案！")
        print("💡 然后可以使用 Frida hook 验证这些方案是否正确。")
    else:
        print("\n💡 所有尝试均失败，可能需要:")
        print("   1. 使用 Frida hook AES 函数获取实际的密钥和 IV")
        print("   2. 检查加密模式是否正确（可能不是 CBC）")
        print("   3. 检查填充方式（可能不是 PKCS7）")
        print("   4. 检查压缩参数（可能不是 -15）")

if __name__ == "__main__":
    main()



